using System.Collections.Generic;
using System.Linq.Expressions;
using System.Reflection;
using Magiq.Insert;
using Magiq.Support;
using Magiq.Update;

namespace Magiq.Sql {
  public class MagiqToSqlInsertProvider : IInsertProvider {
    #region IInsertProvider Members

    public int Execute<F, T>(IQueryableInsert<F, T> insert) where T : class {
      var dataContext = insert.Source.DataContext();
      var newExpression = (MemberInitExpression) insert.NewExpression.Body;
      var assignments = new List<IPropertyValueAssignment>();
      var destinationTable = new TableMapping(newExpression.Type, dataContext);

      foreach (MemberAssignment assignment in newExpression.Bindings) {
        var parameter = assignment.Expression.IsConstant()
                          ? Expression.Parameter(newExpression.Type, "x")
                          : assignment.Expression.GetParameter();
        var lambda = Expression.Lambda(assignment.Expression, parameter);
        var p = new InsertPropertyAssignment((PropertyInfo) assignment.Member, lambda);
        assignments.Add(p);
      }

      if (destinationTable.IsInheritance) {
        var discriminatorProperty = destinationTable.DiscriminatorProperty;
        var valueExpression = Expression.Lambda(Expression.Constant(destinationTable.DiscriminatorCode),
                                                Expression.Parameter(destinationTable.Type, "x"));
        assignments.Add(new InsertPropertyAssignment(discriminatorProperty, valueExpression));
      }

      var queryExecutor = new InsertExecutor(destinationTable.Type, dataContext, insert.Source, assignments);

      var values = queryExecutor.BaseSelectedValues;
      var columns = queryExecutor.Columns;

      var insertSql = "INSERT INTO " + queryExecutor.BaseTableName + " (" + columns.Join(",") +
                      ") SELECT " + values.Join(",") +
                      " FROM " + queryExecutor.BaseWhereClause;

      var value = queryExecutor.ExecuteNonQuery(insertSql);

      return value;
    }

    #endregion
  }
}